package com.hqbzl.chat.protocol;

import com.google.gson.*;
import com.hqbzl.chat.message.RpcResponseMessage;

import java.io.*;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;

public interface Serializer {
    <T> T deserialize(Class<T> clazz, byte[] bytes);

    <T> byte[] serialize(T object);

    enum Algorithm implements Serializer {
        JAVA {
            @Override
            public <T> T deserialize(Class<T> clazz, byte[] bytes) {
                try {
                    final ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
                    final ObjectInputStream ois = new ObjectInputStream(bis);
                    return (T) ois.readObject();
                } catch (Exception e) {
                    throw new RuntimeException("deserizlize exception", e);
                }
            }

            @Override
            public <T> byte[] serialize(T object) {
                try {
                    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    final ObjectOutputStream oos = new ObjectOutputStream(bos);
                    oos.writeObject(object);
                    return bos.toByteArray();
                } catch (IOException e) {
                    throw new RuntimeException("serialize exception", e);
                }
            }
        },
        JSON {
            @Override
            public <T> T deserialize(Class<T> clazz, byte[] bytes) {
                final Gson gson = new GsonBuilder()
                        .registerTypeAdapter(Class.class, new ClassCodec())
                        .registerTypeAdapter(RpcResponseMessage.class, new ObjectCodec())
                        .create();
                final String json = new String(bytes, StandardCharsets.UTF_8);
                return gson.fromJson(json, clazz);
            }

            @Override
            public <T> byte[] serialize(T object) {
                final Gson gson = new GsonBuilder()
                        .registerTypeAdapter(Class.class, new ClassCodec())
                        .registerTypeAdapter(RpcResponseMessage.class, new ObjectCodec())
                        .create();
                final String json = gson.toJson(object);
                return json.getBytes(StandardCharsets.UTF_8);
            }
        }


    }

    static class ObjectCodec implements
            // JsonSerializer<RpcResponseMessage>,
            JsonDeserializer<RpcResponseMessage> {


        @Override
        public RpcResponseMessage deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            RpcResponseMessage msg = null;
            try {
                msg = new RpcResponseMessage();
                final JsonObject asJsonObject = json.getAsJsonObject();
                final int sequenceId = asJsonObject.get("sequenceId").getAsInt();
                final String returnType = asJsonObject.get("returnType").getAsString();

                final Class<?> aClass = Class.forName(returnType);
                final Gson gson = new Gson();
                msg.setSequenceId(sequenceId);
                msg.setReturnType(aClass);
                final JsonElement exception = asJsonObject.get("exception");
                if (exception != null) {
                    final String s = exception.getAsJsonObject().toString();
                    final Exception runtimeException = gson.fromJson(s, Exception.class);
                    msg.setException(runtimeException);
                }else{
                    final JsonElement object = asJsonObject.get("object").getAsJsonObject();
                    final Object o = gson.fromJson(object, aClass);
                    msg.setObject(o);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return msg;
        }

//        @Override
//        public JsonElement serialize(RpcResponseMessage src, Type typeOfSrc, JsonSerializationContext context) {
//            final Class returnType = src.getReturnType();
//            final Object object = src.getObject();
//            final int sequenceId = src.getSequenceId();
//            final Exception exception = src.getException();
//            return null;
//        }
    }

    static class ClassCodec implements JsonSerializer<Class<?>>, JsonDeserializer<Class<?>> {

        @Override
        public Class<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            try {
                final String str = json.getAsString();
                return Class.forName(str);
            } catch (ClassNotFoundException e) {
                throw new JsonIOException(e);
            }
        }

        @Override
        public JsonElement serialize(Class<?> src, Type typeOfSrc, JsonSerializationContext context) {
            return new JsonPrimitive(src.getName());
        }
    }
}
